/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.geo.sim;

import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import technotheist.geo.map.MapUV;
import technotheist.geo.map.scalar.FloatMap;
import technotheist.geo.map.scalar.UByteMap;
import technotheist.geo.map.vector.Float4Map;
import technotheist.geo.map.vector.UnitMap;
import static technotheist.geo.map.MapUtil.*;

/**
 *
 * @author Mitchel Strachan
 */
public class GeoFluidLayer extends GeoLayer {

    GeoFluid[] fluids;
    //
    protected short ctr;
//    final float uvNormalize;
    protected UByteMap counter;
    protected FloatMap energy, energyDelta;
    protected FloatMap density, densityDelta;
    //
//    protected Vector3Map position;
    protected Float4Map velocity, velocityDelta;
    protected Float4Map forceAcc;

    public GeoFluidLayer(GeoFluid[] fluids, UnitMap unit, int size) {
        super(unit, size);
        this.fluids = fluids;
        this.curr = new FluidCell(fluids.length);
        this.nxt = new FluidCell[]{
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),};
    }

    public GeoFluidLayer(GeoFluid[] fluids, int size, GeoLayer lower) {
        super(size, lower);
        this.fluids = fluids;
        this.curr = new FluidCell(fluids.length);
        this.nxt = new FluidCell[]{
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),
            new FluidCell(fluids.length),};
//        this.uvNormalize = volume.uvNormalize;
    }
    final FluidCell curr;
    final int[] currNextIdx = new int[HEX];
    FluidCell[] nxt;

    void load(int p, int u, int v) {
        int idx = position.index(p, u, v);
        position.getNeighbours(idx, currNextIdx);
        curr.load(idx);
        nxt[0].load(currNextIdx[0]);
        nxt[1].load(currNextIdx[1]);
        nxt[2].load(currNextIdx[2]);
        nxt[3].load(currNextIdx[3]);
        nxt[4].load(currNextIdx[4]);
        nxt[5].load(currNextIdx[5]);
    }
    private final Vector3f dir = new Vector3f();

    void solveCurrForce() {
        float cpres = curr.sumP / curr.pos.w;
        float fw = 0;
        int fd = 0;
        curr.fAcc.zero();
        for (int i = 0; i < HEX; i++) {
            if (nxt[i].active) {
                float npres = nxt[i].sumP / nxt[i].pos.w;
                dir.set(curr.pos.x - nxt[i].pos.x,
                        curr.pos.y - nxt[i].pos.y,
                        curr.pos.z - nxt[i].pos.z);
                dir.normalizeLocal();
                dir.multLocal(npres - cpres);
                curr.fAcc.addLocal(dir.x, dir.y, dir.z, 0);
                fw += cpres - npres;
                fd++;
            }
        }
        fw = fw/fd;
        curr.fAcc.w += fw;
    }
    //

    class FluidCell {

        boolean active = true;
        int index;
        float lowerElevation, upperElevation;
        MapUV mp;
        Vector3f nor;
        Vector4f pos;
        Vector4f vel;
        Vector4f fAcc;
        //
//        float vol;
        float e;
        float[] p;
        float sumP;

        private FluidCell(int length) {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }

        void load(int index) {
            if (index < 0) {
                active = false;
                this.index = -1;
                lowerElevation = -1;
                upperElevation = -1;
                return;
            }
            active = true;
            this.index = index;
            position.indexToUV(index, mp);
            unit.get(mp, nor);
            lowerElevation = lowerBound.elevation(mp);
            upperElevation = upperBound.elevation(mp);
            velocity.get(index, vel);
            forceAcc.get(index, fAcc);
            position.get(index, pos);
            e = energy.get(index);
            int i = index * fluids.length;
            sumP = 0;
            for (int f = 0; f < fluids.length; f++) {
                p[f] = density.get(i + f);
                sumP += p[f];
            }
        }

        void save() {
            if (index < 0 || !active) {
                return;
            }
            velocity.set(index, vel);
            forceAcc.set(index, fAcc);
            position.set(index, pos);
            energy.setFloat(index, e);
            int i = index * fluids.length;
            for (int f = 0; f < fluids.length; f++) {
                density.setFloat(i + f, p[f]);
            }
        }
    }

    public static interface GeoFluid {

        public float mass();

        public float density(float energy, float pressure);

        public float advectRate(float energy, float pressure);

        public float diffuseRate(float energy, float pressure);
    }
}
